import React, {
  useEffect,
  useMemo,
  useState,
  useCallback,
  useContext,
} from "react";
import { produce } from "immer";
import "./App.css";
import { Context1 } from "./App";

function FunCom(props) {
  const value = useContext(Context1);
  const [userName, setUserName] = useState("张三");

  // function changeUserName() {
  //   setUserName("李四");
  // }
  useEffect(() => {
    console.log("userName");
  }, [userName]);

  let [arr, setArr] = useState([1, 2, 3, 4, 5]);
  function changeArr() {
    let _arr = [...arr];
    _arr.push(2);
    setArr(_arr);
  }
  let sum = useMemo(() => {
    //计算arr的和
    return arr.reduce((a, b) => a + b, 0);
  }, [arr]);
  //使用 useMemo定义一个对象 不会因为更新而重新创建
  useMemo(() => {
    console.log("obj创建");
    return { name: "张三" };
  }, []);
  //使用callBack定义一个函数 缓存该函数 不会因为更新而重新创建
  let changeUserName = useCallback(() => {
    console.log("changeUserName创建");
    setUserName("李四");
  }, []);

  const [userInfo, setUserInfo] = useState({ name: "张三", age: 18 });
  return (
    <div>
      <h2>hook只能用在函数式组件中，用在别的地方会报错</h2>
      hooks不能放在if、for块状作用域里面，可以在hooks第一个参数回调函数中进行判断
      {/* {props.name} */}
      <h3>useState</h3>
      使用useState来创建响应式数据 useState的方法：返回一个数组：第一项为
      state的数据、 第二项为修改state的方法(调用专用修改方法，传入要修改的值)
      <div>
        tip:useState创建的数据，属于不可变性，如果数据为对象形式，如果要更改对象中某一个属性的值，必须得使用浅合并
        <div>
          【Immutable 不可变对象】
          不直接修改状态或属性对象，而是创建新的对象来代表改变后的状态。
        </div>
      </div>
      <div>
        名字{userInfo.name},年龄{userInfo.age}
        <button
          onClick={() => {
            setUserInfo(
              produce((draft) => {
                //draft指代的是userInfo
                draft.age = draft.age + 1;
                draft.name = "李四";
              })
            );
          }}
        >
          修改用户信息
        </button>
        <div className="code-block">
          {`
          const [userInfo, setUserInfo] = useState({ name: "张三", age: 18 });
              setUserInfo({
              ...userInfo,
              age: userInfo.age + 1,
            });
            `}
          <h3>使用immer修改state的数据</h3>
          <div className="code-block">{`
          安装 npm i immer -S
          
            setUserInfo(
              produce((draft) => {
                //draft指代的是userInfo
                draft.age = draft.age + 1;
                draft.name = "李四";
              })
            );
          
          
          `}</div>
        </div>
      </div>
      <h6>
        用户名:{userName}
        <button onClick={changeUserName}>修改用户名</button>
      </h6>
      <div className="code-block">
        {`  
  const [userName, setUserName] = useState("张三");
  function changeUserName() {
    setUserName("李四");
  }`}
        <hr />
        <h3>useEffect</h3>
        <div className="code-block">
          {` useEffect的用法：useEffect(() => {},
          [])，第一个参数为回调函数，第二个参数为依赖项数组，当依赖项数组中的数据发生变化时，回调函数会执行
          第一个回调函数为必填项
          第二个参数为可选项，
            · 不传递第二个参数，则回调函数在组件挂载ComponentDidMount和更新时ComponentDidUpdate都会执行
            · 传递空数组，则回调函数仅在组件挂载时执行
            · 传递某个数据的数组，类似于vue3中的watchEffect,则回调函数在组件挂载时和依赖项数组中的数据发生变化时都会执行
          `}
        </div>
        <hr />
        <b>useEffect、useMemo、useCallback第二次参数的用法是一致的</b>
        <h3>useMemo 类似vue中的computed</h3>
        一开始执行一次，后续数据有更新才会重新计算，作用
        缓存一个数据，让其不会重新创建（因为当前为函数式组件，每次render更新都会触发函数中的变量和方法重新生成）
        <div>
          当前数字值为{arr},数组的和为{sum}
          <button onClick={changeArr}>数组增加2</button>
        </div>
        <div className="code-block">
          {`
  let [arr, setArr] = useState([1, 2, 3, 4, 5]);
  function changeArr() {
    let _arr = [...arr];
    _arr.push(2);
    setArr(_arr);
  }
  let sum = useMemo(() => {
    //每次arr发生变化时，会重新计算
    return arr.reduce((a, b) => a + b, 0); // 计算arr的和 
  }, [arr]);`}
        </div>
        <hr />
        <h3>useCallback</h3>
        <div>
          因为当前为函数式组件，每次render更新都会触发方法重新生成,使用useCallBack可以缓存方法，避免每次render更新都会重新生成
          <button onClick={useCallback(changeArr, [])}>数组增加2</button>
        </div>
        <h3>useContext</h3>
        使用useContext，孙辈组件不需要在使用Context1.consumer包裹
        <div>APP组件传递过来的数据 - {value}</div>
        <div className="code-block">
          {`
          import {Context1} from "./App"; 
          const value = useContext(Context1);
          {value} //APP组件传递过来的数据`}
        </div>
      </div>
    </div>
  );
}

export default FunCom;
